package cn.yhjz.platform.system.config;

import cn.yhjz.platform.system.entity.LogedUserDetails;
import cn.yhjz.platform.system.entity.SysUser;
import cn.yhjz.platform.system.repository.UserPrincipalRepository;
import cn.yhjz.platform.system.service.ISysUserService;
import cn.yhjz.platform.system.util.LoginUtil;
import cn.yhjz.platform.system.util.TokenUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 添加在springsecurity拦截器之前，使用token获得用户的权限，用来代替原来springsecurity放到session中的操作
 */
@Slf4j
@Component
public class TokenFilter extends OncePerRequestFilter {

    @Autowired
    private TokenUtil tokenUtil;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private UserPrincipalRepository userPrincipalRepository;

    public static String AUTHORIZATION_HEADER = "Authorization";

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String uri = httpServletRequest.getRequestURI();
        //先从header中获得token，如果不存在就从url参数中取
        String authorizationStr = httpServletRequest.getHeader(AUTHORIZATION_HEADER);
        if (StrUtil.isBlank(authorizationStr)) {
            authorizationStr = httpServletRequest.getParameter(AUTHORIZATION_HEADER);
            if (StrUtil.isBlank(authorizationStr)) {
                //如果没有token头，就不用获得用户授权信息了
//                log.info("token为空");
                filterChain.doFilter(httpServletRequest, httpServletResponse);
                return;
            }
        }
        if (authorizationStr != null && authorizationStr.length() > 7) {
            String token = authorizationStr.replaceFirst("Bearer ", "");
            ////////////////////////////////////////////////////////////////////
            SysUser nearUser = tokenUtil.near(token);
            if (null != nearUser) {
                //需要生成新的token
                token = tokenUtil.createToken(nearUser);
                //通过response header的方式返回前端
                httpServletResponse.setHeader("authorization", token);
            }
            SysUser user = tokenUtil.verify(token);
            if (null == user) {
                //如果验证失败，直接退出
                log.info("token验证失败");
//                throw new CredentialsExpiredException("登录已过期");
                filterChain.doFilter(httpServletRequest, httpServletResponse);
                return;
            }
            //从redis中取，
            //如果从redis中取不到就代表过期了，如果能从redis取到，还需要续有效期
            LogedUserDetails principal = userPrincipalRepository.getAndExpire(user.getUserCode());
            if (null == principal) {
//                log.info("token在redis中找不到，或许已过期");
                filterChain.doFilter(httpServletRequest, httpServletResponse);
                return;
            }
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                    principal, null,
                    principal.getAuthorities());
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            //放入线程变量中
            //取出SysUser放入线程变量
            SysUser paramUser = new SysUser();
            paramUser.setUsername(user.getUsername());
            SysUser sysUser = sysUserService.selectOne(paramUser);
            LoginUtil.setCurrentUser(sysUser);
        } else {
            log.error("Authorization格式错误，Authorization:{}", authorizationStr);
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}
